/*
	Written by Nils Kopal (Nils.Kopal(AT)stud.uni-due.de)
	Inspired and some code snipplets from 
	the C# Raytracer written on Apr 3, 2007 by Luke Hoban 
	URL: http://blogs.msdn.com/b/lukeh/archive/2007/04/03/a-ray-tracer-in-c-3-0.aspx
*/
#include <iostream>
#include"Raytracer.h"
#include <time.h>
#include "Logger.h"
#include "ConfigService.h"
#include <sstream> 
#include "util.h"

using namespace std;
using namespace raytracer;
using namespace configService;
using namespace loggingService;
using namespace util;

int main()
{
	int totalwidth;
	int totalheight;
	int width;
	int height;
	int left;
	int top;
	int recursions;
	int supersample;
	string output;
	string outputfile;
	list<Object*> things = list<Object*>();
	list<Light*> lights = list<Light*>();

	Shiny_Surface* shiny = new Shiny_Surface();
	CheckerBoard_Surface* checker = new CheckerBoard_Surface();
	Camera camera;

	try{

		Logger::getLogger()->log(debug,Config::getConfig()->toString());

		string ss = Config::getConfig()->getProperty("supersample");	
		supersample = atoi(ss.c_str());
		if(ss == "" || supersample < 0 || supersample > 1){
			Logger::getLogger()->log(error,"'supersample=" + ss + "' is not possible. Please put a correct value ( 1 or 0 ) into your config file");
			return -1;
		}

		string tw = Config::getConfig()->getProperty("totalwidth");	
		totalwidth = atoi(tw.c_str());
		if(tw == "" || totalwidth <= 0){
			Logger::getLogger()->log(error,"'totalwidth=" + tw + "' is not possible. Please put a correct value ( > 0 ) into your config file");
			return -1;
		}
		
		string th = Config::getConfig()->getProperty("totalheight");
		totalheight = atoi(th.c_str ());
		if(th == "" || totalheight <= 0){
			Logger::getLogger()->log(error,"'totalheight=" + th + "' is not possible. Please put a correct value ( > 0 ) into your config file");
			return -1;
		}

		string w = Config::getConfig()->getProperty("width");	
		width = atoi(w.c_str());
		if(w == "" || width <= 0){
			Logger::getLogger()->log(error,"'width=" + w + "' is not possible. Please put a correct value ( > 0 ) into your config file");
			return -1;
		}
		
		string h = Config::getConfig()->getProperty("height");
		height = atoi(h.c_str ());
		if(h == "" || height <= 0){
			Logger::getLogger()->log(error,"'height=" + h + "' is not possible. Please put a correct value ( > 0 ) into your config file");
			return -1;
		}

		string l = Config::getConfig()->getProperty("left");	
		left = atoi(l.c_str());
		if(l == "" || left < 0){
			Logger::getLogger()->log(error,"'left=" + l + "' is not possible. Please put a correct value ( >= 0 ) into your config file");
			return -1;
		}
		
		string t = Config::getConfig()->getProperty("top");
		top = atoi(t.c_str ());
		if(t == "" || top < 0){
			Logger::getLogger()->log(error,"'top=" + t + "' is not possible. Please put a correct value ( >= 0 ) into your config file");
			return -1;
		}

		string r = Config::getConfig()->getProperty("recursions");
		recursions = atoi(r.c_str());
		if(r == "" || recursions == 0){
			Logger::getLogger()->log(error,"'recursions=" + h + "' is not possible. Please put a correct value ( > 0 ) into your config file");
			return -1;
		}

		output = Config::getConfig()->getProperty("output");
		if(output == ""){
			Logger::getLogger()->log(error,"'output=" + output + "' is not possible. Please put a correct value into your config file");
			return -1;
		}

		string objects = Config::getConfig()->getProperty("objects");
		if(objects==""){
			Logger::getLogger()->log(error,"no objects found in config.ini");
			return -1;
		}
		StringTokenizer tokenizer = StringTokenizer(objects,",");
		while(tokenizer.hasMoreElements())
		{
			string objectname = tokenizer.getNextElement();
			
			string objecttype = Config::getConfig()->getProperty(objectname + ".type");
			if(objecttype == "sphere")
			{
				string position = Config::getConfig()->getProperty(objectname + ".position");
				string radius = Config::getConfig()->getProperty(objectname + ".radius");
				string surface = Config::getConfig()->getProperty(objectname + ".surface");
				Sphere* sphere = new Sphere();
				sphere->position = Vector(position);
				sphere->radius = atof(radius.c_str());				
				if(surface == "shiny")
				{
					sphere->surface = shiny;
				}
				else if(surface=="checker")
				{
					sphere->surface = checker;
				}
				things.push_front(sphere);
			}
			else if(objecttype == "plane")
			{
				string normal = Config::getConfig()->getProperty(objectname + ".normal");
				string offset = Config::getConfig()->getProperty(objectname + ".offset");
				string surface = Config::getConfig()->getProperty(objectname + ".surface");
				Plane* plane = new Plane();
				plane->myNormal = Vector(normal);
				plane->offset = atof(offset.c_str());				
				if(surface == "shiny")
				{
					plane->surface = shiny;
				}
				else if(surface=="checker")
				{
					plane->surface = checker;
				}
				things.push_front(plane);
			}
		}

		string lis = Config::getConfig()->getProperty("lights");
		if(lis==""){
			Logger::getLogger()->log(error,"no lights found in config.ini");
			return -1;
		}
		tokenizer = StringTokenizer(lis,",");
		while(tokenizer.hasMoreElements())
		{
			string lightname = tokenizer.getNextElement();
			string position = Config::getConfig()->getProperty(lightname + ".position");
			string color = Config::getConfig()->getProperty(lightname + ".color");

			Light* light = new Light();
			light->position = Vector(position);
			light->color = Color(color);
			lights.push_front(light);
		}
		
		camera = Camera(Vector(Config::getConfig()->getProperty("camera.position")),
			Vector(Config::getConfig()->getProperty("camera.lookat")));

	}catch(int i){
		if(i == -1){
			cout << "Could not open config.ini";
			return -1;
		}
	}

	Bitmap* bitmap = new Bitmap(width,height);
	Raytracer raytracer = Raytracer(*bitmap,recursions,totalwidth, totalheight,left,top);
	Scene scene = Scene();

	scene.things = things;
	scene.lights = lights;
	scene.camera = camera;

	raytracer.render(scene);
	if(supersample == 1){
		Bitmap* bitmap_supersampled = new Bitmap(width,height);
		bitmap_supersampled->superSampleBitmap(*bitmap);
		bitmap_supersampled->save(output);
	}else{
		bitmap->save(output);
	}

	return 0;
}